<?php

namespace App\Http\Controllers\Api;

use App\Enums\AvailableStatus;
use App\Enums\LotteryRecordStatus;
use App\Exceptions\ApiException;
use App\Http\Controllers\Controller;
use App\Models\Lottery;
use App\Models\LotteryAward;
use App\Models\LotteryRecord;
use App\Models\LotteryUser;
use Illuminate\Contracts\Cache\LockTimeoutException;
use Illuminate\Support\Arr;

class LotteryController extends Controller
{
    /** 查询当前进行中的活动 */
    public function getCurrentLottery()
    {
        /** @var Lottery $lottery */
        $lottery = Lottery::query()
            ->where('agent_id', request()->agent_id)
            ->where('status', AvailableStatus::ENABLED())
            ->first();
        if (!$lottery) {
            return [];
        }

        return [
            'name' => $lottery->name,
            'lottery_id' => $lottery->id,
        ];
    }

    /** 查询活动信息 */
    public function getLottery($lotteryId)
    {
        /** @var Lottery $lottery */
        $lottery = Lottery::query()->find($lotteryId);
        $awards = $lottery->awards->map(function (LotteryAward $award) {
            return [
                'id' => $award->id,
                'name' => $award->name,
                'logo_url' => $award->logo_url,
            ];
        })->toArray();
        for ($i = 0; $i < Lottery::MAX_AWARD_COUNT; ++$i) {
            if (!array_key_exists($i, $awards)) {
                $awards[] = [
                    'id' => 0,
                    'name' => '谢谢参与',
                    'logo_url' => '',
                ];
            }
        }
        $lotteryUser = $this->getLotteryUser($lotteryId);

        return [
            'name' => $lottery->name,
            'start_time' => $lottery->start_time->toDateTimeString(),
            'end_time' => $lottery->end_time->toDateTimeString(),
            'description' => $lottery->description,
            'awards' => Arr::shuffle($awards, $lotteryId),
            'left_times' => $lotteryUser ? max(0, ($lotteryUser->available_times - $this->getUserDrawCount($lotteryUser))) : 0,
        ];
    }

    /** 活动抽奖 */
    public function draw($lotteryId)
    {
        /** @var Lottery $lottery */
        $lottery = Lottery::query()->find($lotteryId);
        if (!$lottery) {
            throw new ApiException('活动不存在');
        }
        if ($lottery->status->isNot(AvailableStatus::ENABLED())) {
            throw new ApiException('抽奖活动未开启');
        }
        if ($lottery->start_time->gt(now())) {
            throw new ApiException('活动尚未开始');
        }
        if ($lottery->end_time->lt(now())) {
            throw new ApiException('活动已经结束');
        }

        $lock = \Cache::lock('lottery_draw_' . $lotteryId, 5);

        try {
            $lock->block(2); // 如果获取不到锁，则等待2秒
            $lotteryUser = $this->getLotteryUser($lotteryId);
            if (!$lotteryUser || $lotteryUser->available_times - $this->getUserDrawCount($lotteryUser) <= 0) {
                throw new ApiException('可抽奖次数不足');
            }
            // 查询所有奖品（奖品级别小于用户级别，且剩余数量大于0)
            /** @var LotteryAward $award */
            $awards = $lottery->awards()
                ->select(['id', 'rate', 'amount', 'draw_count', 'level', 'user_limit'])
                ->whereRaw('amount > draw_count')
                ->where('level', '<=', $lotteryUser->level)
                ->get();
            $drawCountPerAward = LotteryRecord::query()
                ->where('lottery_id', $lotteryId)
                ->where('user_id', auth()->id())
                ->whereRaw('award_id > 0')
                ->select(['award_id', \DB::raw('count(*) as draw_count')])
                ->groupBy(['award_id'])
                ->get()->mapWithKeys(function (LotteryRecord $record) {
                    return [$record->award_id => $record->draw_count];
                });
            $awardsPerLevel = [];
            foreach ($awards as $award) {
                if ($award->user_limit > 0) {
                    $drawCount = Arr::get($drawCountPerAward, $award->id);
                    if ($award->user_limit > $drawCount) {
                        $awardsPerLevel[$award->level][] = $award;
                    }
                } else {
                    $awardsPerLevel[$award->level][] = $award;
                }
            }
            $award = null;
            $levels = array_keys($awardsPerLevel);
            arsort($levels); //从最高级别开始抽奖,如果未中则从低一个级别开始抽
            foreach ($levels as $level) {
                $awardsOfLevel = Arr::get($awardsPerLevel, $level, []);
                $totalRate = array_sum(Arr::pluck($awardsOfLevel, 'rate'));
                if ($totalRate < 100) {
                    $emptyAward = new LotteryAward();
                    $emptyAward->id = 0;
                    $emptyAward->rate = 100 - $totalRate;
                    $awardsOfLevel[] = $emptyAward;
                }

                $award = collect($awardsOfLevel)->flatMap(function (LotteryAward $item) {
                    return array_fill(0, $item->rate, $item);
                })->random();
                \Log::debug('draw_candidate_and_result_' . auth()->id(), [
                    'level' => $level,
                    'candidates' => collect($awardsOfLevel)->toArray(),
                    'award_id' => $award->id,
                ]);
                if (0 != $award->id) {
                    break;
                }
            }
            if ($award && $award->id > 0) {
                $award->increment('draw_count', 1);
                $award->save();
                $awardId = $award->id;
            } else {
                $awardId = 0;
            }
            //创建抽奖参与记录
            $record = new LotteryRecord();
            $record->lottery_id = $lotteryId;
            $record->user_id = auth()->id();
            $record->award_id = $awardId;
            $record->save();
            $lotteryUser->save();

            return [
                'award_id' => $awardId,
                'left_times' => $lotteryUser->available_times - $this->getUserDrawCount($lotteryUser),
            ];
        } catch (LockTimeoutException $e) {
            throw new ApiException('同时抽奖数过多，请稍后再试');
        } finally {
            $lock->release();
        }
    }

    /** 查询我的奖品 */
    public function listMyAwards()
    {
        $page = request()->get('page', 1);
        $limit = request()->get('limit', 10);

        $query = LotteryRecord::query()
            ->where('user_id', auth()->id())
            ->with(['award', 'lottery'])
            ->whereHas('award')
            ->whereHas('lottery')
            ->orderByDesc('id');
        $count = $query->count();
        $records = $query
            ->offset(($page - 1) * $limit)->limit($limit)
            ->get()
            ->map(function (LotteryRecord $record) {
                $award = $record->award;

                $arr = [
                    'award_id' => $award->id,
                    'lottery_name' => $record->lottery->name,
                    'name' => $award->name,
                    'logo_url' => $award->logo_url,
                    'create_time' => $record->created_at->toDateTimeString(),
                    'status' => $record->status->value,
                ];
                $remark = json_decode($record->remark, true);
                if ($record->status->is(LotteryRecordStatus::SENT)) {
                    $arr['express_no'] = Arr::get($remark, 'express_no');
                    $arr['express_name'] = Arr::get($remark, 'express_name');
                }

                return $arr;
            });

        return [
            'count' => $count,
            'list' => $records,
        ];
    }

    /** 查询奖品详情 */
    public function getAward($awardId)
    {
        /** @var LotteryAward $award */
        $award = LotteryAward::query()->find($awardId);
        if (!$award) {
            return [];
        }

        return [
            'name' => $award->name,
            'logo_url' => $award->logo_url,
            'description' => $award->description,
        ];
    }

    /**
     * 获取抽奖用户信息.
     *
     * @param $lotteryId
     *
     * @return LotteryUser
     */
    private function getLotteryUser($lotteryId)
    {
        return LotteryUser::query()
            ->where('lottery_id', $lotteryId)
            ->where('user_id', auth()->id())
            ->first();
    }

    private function getUserDrawCount(LotteryUser $user)
    {
        return LotteryRecord::query()
            ->where('lottery_id', $user->lottery_id)
            ->where('user_id', $user->user_id)
            ->count();
    }
}
